#pragma once
#include<string>
#include<queue>
#include<memory>
#include<mutex>
#include "jdbc/mysql_driver.h"
#include "jdbc/cppconn/prepared_statement.h"
#include "jdbc/cppconn/resultset.h"
#include "jdbc/cppconn/statement.h"
#include "jdbc/cppconn/exception.h"
class MySqlPool {
public:

	//整个函数确定不会被多个线程调用，旨在一个线程调用，主线程调用，创建，所以这里不用枷锁
	MySqlPool(const std::string& url, const std::string& user, const std::string& pass, const std::string& schema, int poolSize)
		: url_(url), user_(user), pass_(pass), schema_(schema), poolSize_(poolSize), b_stop_(false),activeConnections_(0) {
		try {
			for (int i = 0; i < poolSize_; ++i) {
				sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance();
				std::unique_ptr<sql::Connection> con(driver->connect(url_, user_, pass_));
				con->setSchema(schema_);
				pool_.push(std::move(con));
			}
		}
		catch (sql::SQLException& e) {
			// 处理异常
			std::cout << "mysql pool init failed" << std::endl;
		}
	}

	//有可能被多线程调用，所以使用田间变量和锁进行保护
	std::unique_ptr<sql::Connection> getConnection() {

		//首先，锁住
		std::unique_lock<std::mutex> lock(mutex_);

		//进行条件判断，如过不满足，就放开锁，挂起，满足条件:存在元素或者停止信号发射过来，激活所有的线程，，防止线程不退出的场景
		cond_.wait(lock, [this] {
			if (b_stop_) {
				return true;
			}
			return !pool_.empty(); });

		//线程进行到这里存在两种可能：赤字停止了
		if (b_stop_) {
			return nullptr;
		}
		++activeConnections_;
		//赤字没有停止，也就是说我可以获得一个资源，转交资源
		std::unique_ptr<sql::Connection> con(std::move(pool_.front()));
		pool_.pop();
		//返回
		return con;
	}

	//归还资源，有可能被多个线程操作
	void returnConnection(std::unique_ptr<sql::Connection> con) {
		std::unique_lock<std::mutex> lock(mutex_);
		
		//如果加这里，后面直接退出了，也没有通知到析构线程继续进行析构
		--activeConnections_;

		//如果线程此刻察觉，赤字已经停止，那我就不通知也不归还了
		if (b_stop_) {
			cond_.notify_one();		//通知析构线程继续进行析构，同样也会通知到那些依然准备进行消费的消费者线程，但是由于stop，他们依然获取不到，所以会直接退出
			return;
		}

		//归还
		pool_.push(std::move(con));

		//通知
		cond_.notify_one();
	}

	//关闭赤字，通知所有的线程继续执行，在自己的流程中正常结束
	void Close() {
		b_stop_ = true;
		cond_.notify_all();

	}

	//析构整个赤字，这里有点迷茫，这里为什么不要对所有的线程进行回收呢？
	//一般是直接分离的，这里也不是线程池，所以不用在这里做事
	//为什么这里不直接通知关闭池子？这样的花你在析构之前还得要通知关闭赤字
	~MySqlPool() {
		Close();

		//所以这里依然有问题，就是对于那些没有获取到资源的线程来说，直接Close是可以通知到他们，从而让他们退出
		//但是对于那些已经获取到资源的线程来说，假设这里直接结束并且已经退出了
		//那么他们如果再使用return时，就会发生空悬指针，因此，应该保证这样的事，就说所有的线程，包括使用池中元素的
		//线程全部都已经归坏或者结束时，才可以去析构当前的池子
		//1.使用enable——self,把连接给他的同时把自己传进去，保证该线程不死时，我也不会死
		//2.使用 std::atomic<int> activeConnections_;  // 活跃连接数，用于等待析构时连接池的完全关闭
		//每次借出去就++，每次换回来就--，从而保证不会出现空悬指针
		std::unique_lock<std::mutex> lock(mutex_);


		//这里通知后，我要等待所有的活跃线程结束
		cond_.wait(lock, [this] { return activeConnections_ == 0; });  // 等待所有活跃连接归还
		while (!pool_.empty()) {
			pool_.pop();
		}
	}

private:

	//mysql服务器的IP，user.passwd
	std::string url_;
	std::string user_;
	std::string pass_;
	std::string schema_;
	int poolSize_;											//池子大小
	std::queue<std::unique_ptr<sql::Connection>> pool_;		//池：里面是指针，指向一个堆上存在的连接，只可以被一个人所拥有，不可以被共享，mysql连接是不可以被共享的，提交事务会出错
	std::mutex mutex_;										//锁，宝华赤字和形成消费者-生产者模型
	std::condition_variable cond_;							//条件变量，当池中没有元素的时候，放开锁，挂起，等待生产者条件变量通知
	std::atomic<bool> b_stop_;								//停止整个赤字
	std::atomic<int> activeConnections_;  // 活跃连接数
};

//close:有好几种情况：1.消费者线程存在否    2.生产者线程存在否     3.析构线程
//1.还有消费者线程，也有生产者线程，依然有析构线程，但是此时池子停止
//消费者线程被唤醒：close唤醒，生产者唤醒，无论哪种，都会由于stop，进而结束
//生产者线程：会唤醒消费者线程，进而导致其结束，也会唤醒析构线程，保证继续析构
//析构线程，每次被唤醒，就会检查下是不是所有的都被归还，否则继续等待，知道所有的都被归还之后才会退出
//最终保证：在所有已经借出去的资源都没有归还之前，我是绝对不会退出的，用这个可以完善线程池，只要外面还有正在执行的任务，我就不会退出，只要队列里还有任务，我就不会退出
//这是一种保证时的
//所以如果是我刚刚提到的那种线程池，你该如何做？
//首先等待所有正在执行工作的线程，回收他们
//其次检查是否是已经结束了，如果没有结束，那就开一个单线程保证所有都结束？
//有没有其他办法，如果队列很大，如和保证全部退出？
//1.stop线程池时，等待一个信号，停止信号stopOK&&队列为空，只要队列不为空，就不结束所有线程，从而一直执行
//2.谁来激活这个信号？每消费一个线程就来检查下，那么会不会一场停止呢？不会因为检查的时（停止信号stopOK&&队列为空），所以只要停止信号stop不OK，也就没有办法异常停止
//3.最终，将会保证所有的线程池消费完所有已经加入到队列的任务全部都消费完，才去结束，这是一种乐观的线程池
//也有消极的线程池，，并不会保证所有在线程队列中的任务结束才结束的，也可以



